/**
 * Copyright (c) 2010
 * Bert Young. UESTC. 
 */

#include <poll.h>
#include <errno.h>
#include "Thread.h"
#include "MsgBuffer.h"
#include "TCPClientThread.h"

bool TCPClientThread::connect( const char * servip, const unsigned short port)
{
	this->serverIP = servip;
	this->serverPort = port;

	int fd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP );
	if ( fd < 0 )
		return false;

	socklen_t winsize = 128 * 1024;
	int ret = ::setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &winsize, sizeof(winsize));
	if ( ret < 0 )
	{
		TEMP_FAILURE_RETRY(::close(fd));
		return false;
	}
	ret = ::setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &winsize, sizeof(winsize));
	if ( ret < 0 )
	{
		TEMP_FAILURE_RETRY(::close(fd));
		return false;
	}

	struct sockaddr_in serv;
	bzero(&serv, sizeof(serv));
	serv.sin_family = AF_INET;
	serv.sin_addr.s_addr = inet_addr(serverIP.c_str());
	serv.sin_port = htons(port);

	ret = TEMP_FAILURE_RETRY(::connect(fd,(struct sockaddr*)&serv,sizeof(serv)));
	if ( ret < 0 )
	{
		TEMP_FAILURE_RETRY(::close(fd));
		return false;
	}

	this->pSocket = new SocketConnection(fd, &serv);
	if ( pSocket == NULL )
	{
		TEMP_FAILURE_RETRY(::close(fd));
		return false;
	}
	//connect server success!
	return true;
}

bool TCPClientThread::sendPacket(const void *cmd, unsigned int len)
{
	if ( pSocket != NULL )
		return pSocket->sendPacket(cmd, len, buffered);
	else
		return false;
}
void TCPClientThread::run()
{
	this->buffered = true;

	struct pollfd pfds, pfds_read;

	pfds.fd = this->pSocket->localSock;
	pfds.events = POLLIN | POLLOUT | POLLPRI;
	pfds.revents = 0;

	pfds_read.fd = this->pSocket->localSock;
	pfds_read.events = POLLIN | POLLPRI;
	pfds_read.revents = 0;
	long time = 50; //mill seconds
	struct timespec start, end;
		
	while ( isAlive() )
	{
		clock_gettime(CLOCK_REALTIME, &start);
		if ( TEMP_FAILURE_RETRY(::poll(&pfds_read, 1, time ) ) > 0)
		{
			if ( pfds_read.revents & POLLERR )
				break;
			else if ( pfds_read.revents & POLLIN )
			{
				if ( !this->recv() )
				{
					debug("error1 recv");
					break;
				}
			}
		}
		clock_gettime(CLOCK_REALTIME, &end);
		unsigned long begin = start.tv_sec*1000L + start.tv_nsec/1000000L;
		unsigned long over = end.tv_sec*1000L + end.tv_nsec/1000000L;
		time -= (over-begin);
		if ( time <= 0 )
		{
			time = 50;
			if ( TEMP_FAILURE_RETRY(::poll(&pfds, 1, 0) )>0)
			{
				if ( pfds.revents & POLLERR )
					break;
				else
				{
					if ( pfds.revents & POLLIN )
						if ( !this->recv() )
						{
							debug("error2 recv");
							break;
						}

					if ( pfds.revents & POLLOUT )
						if ( !this->send() )
						{
							debug("error3 send");
							break;
						}
				}
			}
		}
	}

	finalSync();
	this->buffered = false;
}
void TCPClientThread::finalSync()
{
	if ( pSocket != NULL )
		pSocket->finalSync();
}
bool TCPClientThread::send()
{
	if ( pSocket != NULL )
		return pSocket->sync();
	else
		return false;
}
bool TCPClientThread::recv()
{
	int nBytes = pSocket->recv();
	if ( nBytes < 0 )
	{
		debug("%s, nBytes = %d", __PRETTY_FUNCTION__, nBytes);
		return false;
	}
		
	unsigned char cmd[SocketConnection::MAX_DATASIZE];
	do
	{
		nBytes = this->pSocket->recvPacketNoPoll(cmd);
		if ( nBytes > 0 )
		{
			debug("%s = recv length %d, %.*s",__PRETTY_FUNCTION__, nBytes, nBytes, (char *)cmd);
			NullBaseCmd * pCmd = (NullBaseCmd *)cmd;
			if ( pCmd->byCmd == NULL_CMD )
			{
				if ( pCmd->byParam == SERVER_TEST_PARAM)
				{
					ServerTestCmd testserver;
					if ( this->sendPacket(&testserver, sizeof(ServerTestCmd)) )
						debug("echo server success!!!");
					else
					{
						debug("echo server failed !!!");
						return false;
					}
				}
			}
			else
			//if testcmd, echo it;
				parse(cmd, nBytes);
		}
		else
			return true;
	}while ( true );

	return true;
}

void TCPClientThreadNoBuffer::parse(const void * cmd, unsigned int len)
{
	msgParse(cmd, len);
}

void TCPClientThreadBuffer::parse(const void * cmd, unsigned int len)
{
	MsgBuffer::put(cmd, len);
}

